function _newXMLDocument() {
   return Components.classes["@mozilla.org/xml/xml-document;1"].createInstance().QueryInterface(Components.interfaces.nsIDOMXMLDocument);
}

var Collection = {
   collection: null,
   categories: null,
   links: null,
   feedTree: null,
   feedRDF: null,
   xsltCache: null,
   currentDropObserver: null,
   messages: null,
   titleE: null,
   title: null,
   subtitleE: null,
   subtitle: null,
   plugins: null,
   copiedEntry: null,
   init: function(data) {
      LOG.level = data.logLevel;
      document.title = data.title;
      this.collection = data.collection;
      this.categories = data.categories;
      this.xsltCache = data.xsltCache;
      this.timeout = data.timeout;
      this.plugins = data.plugins;
      this.currentDropObserver = this.urlDropObserver;
      this.messages = new MessageBundle(document.getElementById('collection-messages'));
      this.feedTree = document.getElementById("feed-tree");
      this.feedRDF = new RDFSource(this.feedTree.database);
      this.titleE = document.getElementById('title');
      this.subtitleE = document.getElementById('subtitle');
      this.initPlugins();
      this.tabbox = document.getElementById('feed-info');
      this.categoryManager = new Categories(document.getElementById('categories'));
      this.links = new Links(document.getElementById('links'));
      if (this.collection.feed.loaded) {
         this.refresh();
      } else {
         var current = this;
         this.collection.feed.load({
            onSuccess: function() {
               current.refresh();
            },
            onFailure: function() {
               alert("Cannot load feed "+this.collection.feed.url);
            }
         });
      }
      document.getElementById("content").hidden = false;
   },
   initPlugins: function() {
      var entryToolbar = document.getElementById('entry-toolbar');
      var lastChild = entryToolbar.firstChild;
      while (lastChild.localName!="spacer") {
         lastChild = lastChild.nextSibling;
      }
      for (var pid in this.plugins) {
         //alert("Loading plugin "+pid);
         var plugin = this.plugins[pid];
         for (var id in plugin.entryToolbarItems) {
            var editor = plugin.entryToolbarItems[id];
            if (!editor) {
               continue;
            }
            var button = document.createElementNS(XUL_NS,"toolbarbutton");
            button.setAttribute("label",editor.label);
            button.setAttribute("tooltiptext",editor.description);
            button.setAttribute("oncommand","Collection.addWithPlugin('"+pid+"','"+id+"')");
            entryToolbar.insertBefore(button,lastChild);
         }
      }
   },
   refresh: function() {
      this.title =this.collection.feed.getTitle(true);
      this.titleE.value = this.title.xml.textContent;
      this.subtitle =this.collection.feed.getSubtitle();
      if (this.subtitle) {
         this.subtitleE.value = this.subtitle.xml.textContent;
      }
      var categories = this.collection.feed.getCategories();
      this.categoryManager.clear();
      for (var i=0; i<categories.length; i++) {
         this.categoryManager.addCategory(
            categories[i].scheme,
            categories[i].term,
            categories[i].value
         );
      }
      var links = this.collection.feed.getLinks();
      this.links.clear();
      for (var i=0; i<links.length; i++) {
         if (links[i].rel!="edit" && links[i].rel!="self") {
            this.links.addLink(
               links[i].rel,
               links[i].href,
               links[i].type,
               links[i].title
            );
         }
      }
      this.feedRDF.clear();
      var xslt = this.xsltCache.transformers["feed-tree"];
      xslt.setParameter("","root","http://www.atomojo.org/O/atomic/feed");
      var rdfDocument = xslt.transformToDocument(this.collection.feed.xml.ownerDocument);
      LOG.logDocument(Logger.FINE,rdfDocument);
      this.feedRDF.replaceSubjectFromDocument("id:feed",rdfDocument);
      this.feedTree.builder.rebuild();
   },
   addCategory: function(scheme,term,value) {
      term = (term+"").trim();
      if (term.length==0) {
         return;
      }
      this.headerChanged();
      scheme = (scheme+"").trim();
      this.categoryManager.addCategory(scheme,term,value);
   },
   addLink: function(rel,href,type,title) {
      href = (href+"").trim();
      if (href.length==0) {
         return;
      }
      this.headerChanged();
      this.links.addLink(rel,href,type,title);
   },
   addWithPlugin: function(pid,id) {
      var plugin = this.plugins[pid];
      var editor = plugin.entryEditors[id];
      var entry = this.collection.feed.addEntry("untitled");
      //alert(editor.xul);
      this.editEntry(entry,editor.xul);
   },
   urlDropObserver: {
      getSupportedFlavours : function () {
         var flavours = new FlavourSet();
         flavours.appendFlavour("application/x-moz-url");
         flavours.appendFlavour("application/x-moz-file","nsIFile");
         flavours.appendFlavour("text/unicode");
         return flavours;
      },
      onDragOver: function (event,flavour,session){},
      onDrop: function (event,dropdata,session){
         //alert(dropdata.data);
         if (dropdata.data!=""){
            //alert(dropdata.flavour.contentType+"\n"+dropdata.data);
            if (dropdata.flavour.contentType=="application/x-moz-file") {
               //alert("file://"+dropdata.data.path);
               Collection.uploadResource("file://"+dropdata.data.path);
            } else {
               var urls = dropdata.data.split(/\s+/);
               var ignored = new Array();
               for (var i=0; i<urls.length; i++) {
                  if (urls[i].match(/\S+:\S+/)) {
                     // Upload content if file
                     var url = ""+urls[i];
                     if (url.match(/^file:/)) {
                        Collection.uploadResource(url);
                     } else {
                        Collection.addLinkEntry(url);
                     }
                  } else if (urls[i].length>0) {
                     ignored[ignored.length] = urls[i];
                  }
               }
               if (ignored.length!=0) {
                  var msg="Ignored the following text:\n";
                  for (var i=0; i<ignored.length; i++) {
                     msg += ignored[i]+"\n";
                  }
                  LOG.warn(msg);
               }
            }
         }
      }
   },
   entryDropObserver: {
      currentRow: -1,
      getSupportedFlavours : function () {
         var flavours = new FlavourSet();
         flavours.appendFlavour("application/x-moz-url");
         flavours.appendFlavour("application/x-moz-file","nsIFile");
         flavours.appendFlavour("text/unicode");
         return flavours;
      },
      onDragOver: function (event,flavour,session){ 
         var row = { }
         var col = { }
         var child = { };
         Collection.feedTree.treeBoxObject.getCellAt(event.pageX, event.pageY, row, col, child);
         if (row.value>=0) {
            Collection.feedTree.view.selection.clearSelection();
            Collection.feedTree.view.selection.select(row.value);
            this.currentRow = row.value;
         }
      },
      onDrop: function (event,dropdata,session){
         if (this.currentRow<0) {
            return;
         }
         var id = Collection.feedTree.view.getCellText(Collection.feedTree.currentIndex,Collection.feedTree.columns.getColumnAt(0));
         var type = Collection.feedTree.view.getCellText(Collection.feedTree.currentIndex,Collection.feedTree.columns.getColumnAt(1));
         if (type=="link" || type=="other" || type=="category" || type=="title") {
            this.currentRow = Collection.feedTree.view.getParentIndex(this.currentRow);
            id = Collection.feedTree.view.getCellText(this.currentRow,Collection.feedTree.columns.getColumnAt(0));
            type = Collection.feedTree.view.getCellText(this.currentRow,Collection.feedTree.columns.getColumnAt(1));
         }
         //alert(dropdata.data);
         if (dropdata.data!=""){
            //alert(dropdata.flavour.contentType+"\n"+dropdata.data);
            if (dropdata.flavour.contentType=="application/x-moz-file") {
               Collection.updateResource(id,"file://"+dropdata.data.path);
            } else {
               var urls = dropdata.data.split(/\s+/);
               var ignored = new Array();
               if (urls.length>1) {
                  alert("Multiple files cannot update one entry.");
               }
               if (urls[0].match(/\S+:\S+/)) {
                  // Upload content if file
                  var url = ""+urls[0];
                  if (url.match(/^file:/)) {
                     Collection.updateResource(id,url);
                  } else {
                     alert("Updating from non-file URLs is not supported:\n"+url);
                  }
               }
            }
         }
      }
   },
   textDropObserver: {
      getSupportedFlavours : function () {
         var flavours = new FlavourSet();
         flavours.appendFlavour("text/unicode");
         return flavours;
      },
      onDragOver: function (event,flavour,session){},
      onDrop: function (event,dropdata,session){
         if (dropdata.data!=""){
            Collection.addTextEntry(dropdata.data);
         }
      }
   },
   htmlDropObserver: {
      getSupportedFlavours : function () {
         var flavours = new FlavourSet();
         flavours.appendFlavour("text/html");
         return flavours;
      },
      onDragOver: function (event,flavour,session){},
      onDrop: function (event,dropdata,session){
         if (dropdata.data!=""){
            Collection.addXHTMLEntry(dropdata.data);
         }
      }
   },
   uploadResource: function(url) {
      var urlObject =  _ioService.newURI(url,null,null);
      if (urlObject.scheme!="file") {
         alert("Cannot upload non-file URLs");
         return;
      }
      var fileURL = urlObject.QueryInterface(Components.interfaces.nsIFileURL);
      if (confirm("Upload file "+fileURL.file.path+" ?")) {
         var currentApp = this;
         var progressDialog = window.openDialog(
               'chrome://atomic/content/progress.xul','progress'+(new Date()).getTime(),'centerscreen,chrome,resizable',
               {
                  message: "Uploading "+fileURL.file.path,
                  inprogress: request
               }
            );
         progressDialog.focus();
         var request = this.collection.feed.addResourceFromFile(fileURL.file.path,
         {
            timeout: currentApp.timeout,
            onSuccess: function() {
               currentApp.refresh();
               progressDialog.close();
            },
            onFailure: function(status,doc,text) {
               progressDialog.close();
               alert(currentApp.messages.format("uploadFailed",fileURL.file.path,status,doc ? doc.documentElement.textContent.substring(0,100) : text));
            }
         });
      
      
         
      }
   },
   updateResource: function(id,url) {
      var entry = this.collection.feed.entries[id];
      var content = entry.getContent();
      if (!content.src) {
         alert("This entry is not a media entry and cannot be updated by drag-n-drop.");
         return;
      }
      var urlObject =  _ioService.newURI(url,null,null);
      if (urlObject.scheme!="file") {
         alert("Cannot upload non-file URLs");
         return;
      }
      var fileURL = urlObject.QueryInterface(Components.interfaces.nsIFileURL);
      if (confirm("Are you sure you want to update this entry's media "+content.src+" with file "+fileURL.file.path+"?")) {
         var currentApp = this;
      
         var progressDialog = window.openDialog(
               'chrome://atomic/content/progress.xul','progress'+(new Date()).getTime(),'centerscreen,chrome,resizable',
               {
                  message: "Updating "+content.src+" with "+fileURL.file.path,
                  inprogress: request
               }
            );
         progressDialog.focus();
         var request = content.updateFromFile(fileURL.file.path,{
            timeout: currentApp.timeout,
            onSuccess: function() {
               progressDialog.close();
               //alert("Upload of "+fileURL.file.path+" is complete.");
            },
            onFailure: function(status,doc,text) {
               progressDialog.close();
               alert(currentApp.messages.format("uploadFailed",fileURL.file.path,status,doc ? doc.documentElement.textContent.substring(0,100) : text));
            }
         });

      }
   },
   addLinkEntry: function(url) {
      var entry = this.collection.feed.addEntry("untitled");
      entry.addLink("alternate",url);
      this.editEntry(entry);
   },
   addTextEntry: function(text) {
      var entry = this.collection.feed.addEntry("untitled");
      var content = entry.getContent(true);
      content.type = "text";
      content.setTextContent(text);
      this.editEntry(entry);
   },
   addXHTMLEntry: function(xhtml) {
      try {
         var divWrapped = "<div xmlns='http://www.w3.org/1999/xhtml'>"+xhtml+"</div>";
         var xhtmlParser = new HTMLParser();
         xhtmlParser.tidy = true;
         xhtmlParser.init(_newXMLDocument(),null,divWrapped);
         xhtmlParser.parseDocument();
         
         var entry = this.collection.feed.addEntry("untitled");
         var content = entry.getContent(true);
         content.type = "xhtml";
         content.setNodeContent(xhtmlParser.document.documentElement);
         this.editEntry(entry);
         
         /*
         var xmlParser = new DOMParser();
         var xmlDocument = xmlParser.parseFromString(divWrapped, 'text/xml');
         var roottag = xmlDocument.documentElement; 
         if ((roottag.tagName == "parsererror") &&
             (roottag.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml")) {
            alert(this.messages.format("parseFailed",roottag.firstChild.textContent));
         } else {
            // we've got a good bit, check for extra div
            if (roottag.childNodes.length==1 &&
                roottag.firstChild.localName=="div" && 
                roottag.firstChild.namespaceURI=="http://www.w3.org/1999/xhtml") {
               roottag = roottag.firstChild;
            }
            var entry = this.collection.feed.addEntry("untitled");
            var content = entry.getContent(true);
            content.type = "xhtml";
            content.setNodeContent(roottag);
            this.editEntry(entry);
         }
         */
      } catch (e) {
         alert(this.messages.format("parseFailed",e));
      }
   },
   doCopy: function() {
      //alert("copy");
      var id =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(0));
      var type =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(1));
      LOG.debug("Copy on "+id+" "+type);
      if (type=="entry") {
         this.copiedEntry = this.collection.feed.entries[id];
         if (!this.copiedEntry) {
            throw "Cannot find entry with id "+id;
         }
      }
   },
   doPaste: function() {
      //alert("paste");
      var id =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(0));
      var type =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(1));
      LOG.debug("Paste on "+id+" "+type);
      if (type=="entry" && this.copiedEntry) {
         //alert("Pasting copied entry "+this.copiedEntry.id);
         try {
            var entry = this.collection.feed.copyEntry(this.copiedEntry);
            var current = this;
            try {
               entry.save({
                  timeout: current.timeout,
                  onSuccess: function() {
                     current.refresh();
                  },
                  onFailure: function(status,doc,text) {
                     alert(current.messages.format("addFailed",status,doc ? doc.documentElement.textContent.substring(0,100) : text));
                     current.collection.feed.removeEntry(entry);
                  }
               });
            } catch (ex) {
               alert("Failed to save due to: "+ex);
            }
         } catch (ex) {
            alert("Cannot duplicate entry do to: "+ex);
         }
      }
   },
   doDelete: function() {
      if (this.tabbox.selectedIndex==2) {
         if (confirm("Are you sure you want to delete the selected links?")) {
            this.deleteSelectedLinks();
         }
      } else if (this.tabbox.selectedIndex==1) {
         if (confirm("Are you sure you want to delete the selected categories?")) {
            this.deleteSelectedCategories();
         }
      } else if (this.tabbox.selectedIndex==0) {
         var id =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(0));
         var type =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(1));
         LOG.debug("Delete on "+id+" "+type);
         if (type=="entry") {
            var entry = this.collection.feed.entries[id];
            if (!entry) {
               throw "Cannot find entry with id "+id;
            }
            if (!confirm("Are you sure you want to remove the entry titled:\n"+entry.getTitle().xml.textContent)) {
               return;
            }
            LOG.debug("Deleting entry "+id);
            var current = this;
            LOG.debug("DELETE "+entry.editURL);
            this.collection.feed.removeEntry(entry, {
               timeout: current.timeout,
               onSuccess: function() {
                  current.refresh();
               },
               onFailure: function(status,doc) {
                  var msg = current.messages.format("deleteFailed",id,status,doc ? doc.documentElement.textContent.substring(0,100) : "");
                  alert(msg);
               }
            }
            );
         }
      }
   },
   doDoubleClick: function() {
      var id =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(0));
      var type =this.feedTree.view.getCellText(this.feedTree.currentIndex,this.feedTree.columns.getColumnAt(1));
      LOG.debug("Double click on "+id+" "+type);
      if (type=="link" || type=="other" || type=="category" || type=="title") {
         var parent = Collection.feedTree.view.getParentIndex(this.feedTree.currentIndex);
         id = Collection.feedTree.view.getCellText(parent,this.feedTree.columns.getColumnAt(0));
         type = Collection.feedTree.view.getCellText(parent,this.feedTree.columns.getColumnAt(1));
         LOG.debug("Moved to parent "+id+" "+type);
      }
      if (type=="entry") {
         this.editEntry(this.collection.feed.entries[id]);
      }
   },
   upload: function() {
      var current = this;
      window.openDialog("chrome://atomic/content/upload.xul","upload-"+this.collection.feed.getId(),"chrome,resizable",{
         title: "Upload Resources - "+this.collection.feed.getTitle().xml.textContent,
         feed: this.collection.feed,
         onUpload: function() {
            try {
               current.refresh();
            } catch (ex) {
               LOG.severe("Cannot refresh: "+ex);
            }
         }
      });
   },
   addEntry: function() {
      var entry = this.collection.feed.addEntry("untitled");
      this.editEntry(entry);
   },
   reload: function() {
      var oldXML = this.collection.feed.xml;
      var oldEntries = this.collection.feed.entries;
      var currentApp = this;
      this.collection.feed.load({
         timeout: currentApp.timeout,
         onSuccess: function() {
            currentApp.refresh();
         },
         onFailure: function(status,msgDoc) {
            currentApp.collection.feed.xml = oldXML;
            currentApp.collection.feed.entries = oldEntries;
            alert(currentApp.messages.format("failedToLoad",currentApp.collection.feed.url,status,msgDoc.substring(0,100)));
         }
      });
   },
   view: function() {
   },
   editEntry: function(entry,url) {
      var current = this;
      window.openDialog(url ? url : "chrome://atomic/content/edit-entry.xul","entry-"+entry.id,"chrome,resizable",{
         title: "Entry - "+entry.getTitle().xml.textContent,
         entry: entry,
         doSave: function(response) {
            try {
               entry.save({
                  timeout: current.timeout,
                  onSuccess: function() {
                     current.refresh();
                     response.onSuccess();
                  },
                  onFailure: function(status,doc,text) {
                     response.onFailure(current.messages.format("addFailed",status,doc ? doc.documentElement.textContent.substring(0,100) : text));
                  }
               });
            } catch (ex) {
               alert(ex);
            }
         },
         doCancel: function() {
            if (entry.local) {
               current.collection.feed.removeEntry(entry);
            }
         }
      });
   },
   headerChanged: function() {
      this.disableHeaderToolbar(false);
   },
   disableHeaderToolbar: function(flag) {
      document.getElementById("header-save").disabled = flag;
      document.getElementById("header-revert").disabled = flag;
   },
   deleteSelectedCategories: function() {
      this.headerChanged();
      this.categoryManager.deleteSelected();
   },
   deleteSelectedLinks: function() {
      this.headerChanged();
      this.links.deleteSelected();
   },
   saveHeader: function() {
      this.title.setTextContent(this.titleE.value);
      if (this.subtitleE.value) {
         if (!this.subtitle) {
            this.subtitle = this.collection.feed.getSubtitle(true);
         }
         this.subtitle.setTextContent(this.subtitleE.value);
      } else if (this.subtitle) {
         this.collection.feed.xml.removeChild(this.subtitle.xml);
      }
      this.collection.feed.removeAllCategories();
      var set = this.categoryManager.getCategories();
      for (var i=0; i<set.length; i++) {
         this.collection.feed.addCategory(set[i].scheme,set[i].term,set[i].value);
      }
      this.collection.feed.removeAllLinks();
      set = this.links.getLinks();
      for (var i=0; i<set.length; i++) {
         this.collection.feed.addLInk(set[i].rel,set[i].href,set[i].type,set[i].title);
      }
      var currentApp = this;
      this.collection.feed.updateByPut(
      {
         timeout: currentApp.timeout,
         onSuccess: function() {
            currentApp.disableHeaderToolbar(true);
         },
         onFailure: function(status,msgDoc,text) {
            alert(currentApp.messages.format("failedToSaveHeader",status,text.substring(0,100)));
         }
      });
   },
   revertHeader: function() {
      this.titleE.value = this.title.xml.textContent;
      if (this.subtitle) {
         this.subtitleE.value = this.subtitle.xml.textContent;
      } else {
         this.subtitleE.value = null;
      }
      this.categoryManager.clear();
      this.links.clear();
      var categories = this.collection.feed.getCategories();
      for (var i=0; i<categories.length; i++) {
         this.categoryManager.addCategory(
            categories[i].scheme,
            categories[i].term,
            categories[i].value
         );
      }
      var links = this.collection.feed.getLinks();
      for (var i=0; i<links.length; i++) {
         if (links[i].rel!="edit" && links[i].rel!="self") {
            this.links.addLink(
               links[i].rel,
               links[i].href,
               links[i].type,
               links[i].title
            );
         }
      }
      this.disableHeaderToolbar(true);
   }
  
   
}